package com.plugin.sproutcore.ui.widgets;

import java.io.IOException;
import java.util.List;
import java.util.TreeSet;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

import com.plugin.sproutcore.core.resources.SproutCoreContext;
import com.plugin.sproutcore.core.resources.natures.SproutCoreNature;
import com.plugin.sproutcore.core.resources.properties.PropertiesLoader;
import com.plugin.sproutcore.core.resources.properties.SproutCoreProject;
import com.plugin.sproutcore.core.resources.properties.SproutCoreProperties;
import com.plugin.sproutcore.core.validator.IValidate;
import com.plugin.sproutcore.ui.events.ChangeProjectEvent;
import com.plugin.sproutcore.ui.events.ChangeProjectListener;
import com.plugin.sproutcore.ui.events.ChangeSelectionEvent;
import com.plugin.sproutcore.ui.events.ChangeSelectionListener;
import com.plugin.sproutcore.ui.events.EventDispatcher;
import com.plugin.sproutcore.util.ProjectUtil;

public class ProjectSelector extends EventDispatcher implements IValidate
{
	private final ProjectBrowserController projectBrowser;
	private final ProjectGroup projectGroup;
	private final AppGroup appGroup;
	
	private Text containerText;
	
	private SproutCoreContext context;
	private SproutCoreProperties properties;
	private GridLayout layout;
	
	public ProjectSelector(Object dialog, ISelection selection) 
	{
		IProject project = ProjectUtil.getProjectFromSelection(selection);
		this.context = new SproutCoreContext(project);
		
		this.projectBrowser = new ProjectBrowserController(dialog, project);
		this.projectGroup = new ProjectGroup();
		this.appGroup = new AppGroup();
	}
	
	public void createControl(final Composite parent) 
	throws CoreException
	{
		createControl(parent, new GridData(GridData.FILL_HORIZONTAL));
	}
	
	public void createControl(final Composite parent, int numColumns) 
	throws CoreException
	{
		final GridData gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalSpan = numColumns;
		createControl(parent, gd);
	}

	public void createControl(final Composite parent, final GridData gd) 
	throws CoreException 
	{
		this.properties = context.getProperties();
		
		final Composite container = new Composite(parent, SWT.NULL);
		container.setLayoutData(gd);
		
		if (this.getLayout() != null) {
			container.setLayout(this.getLayout());
		}
		
		Label label = new Label(container, SWT.NONE);
		label.setText("&Project:");
		
		containerText = new Text(container, SWT.BORDER | SWT.SINGLE);
		containerText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		
		IProject project = getProject();
		if (project != null && project.exists()) 
		{
			containerText.setText(project.getFullPath().toOSString());
		}
		
		containerText.addModifyListener(new ModifyListener() 
		{
			public void modifyText(ModifyEvent e)  
			{
				tryToChangeProject();
			}
		});
		
		Button button = new Button(container, SWT.PUSH);
		button.setText("Browse...");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				try {
					handleBrowse();
				} catch (CoreException ex) {
					ex.printStackTrace();
				}
			}
		});
		
		projectGroup.createControl(container, getLayout().numColumns);
		appGroup.createControl(container, getLayout().numColumns);
		
		//handleBrowse();
		refresh();
	}
	
	public GridLayout getLayout()
	{
		if (layout == null) {
			layout = new GridLayout(3, false);
			layout.marginWidth = 0;
		}
		return layout;
	}
	
	public void addChangeProjectListener(ChangeProjectListener listener)
	{
		addListener(ChangeProjectListener.CHANGE, listener);
	}
	
	public void addChangeSelectionListener(ChangeSelectionListener listener)
	{
		addListener(ChangeSelectionListener.CHANGE, listener);
	}
	
	private void tryToChangeProject()
	{
		IResource resource = getResource();
		if (resource != null && resource.exists()) {
			try {
				changeProject(resource.getProject());
			} catch (CoreException e) {
				e.printStackTrace();
			}
		}
		dispatchEvent(ChangeProjectListener.CHANGE, new ChangeProjectEvent(this, getProject()));
	}
	
	private IResource getResource()
	{
		return ResourcesPlugin.getWorkspace().getRoot().findMember(containerText.getText());
	}
	
	private void changeProject(IProject project) throws CoreException
	{
		projectBrowser.setResource(project);
		
		this.context = new SproutCoreContext(getProject());
		this.properties = context.getProperties();
		
		this.refresh();
	}
	
	private void handleBrowse() throws CoreException
	{
		projectBrowser.handleBrowse();
		IResource resource = projectBrowser.getResource();
		if (resource != null) {
			containerText.setText(resource.getProject().getFullPath().toString());
		}
	}
	
	private void refresh() throws CoreException
	{
		projectGroup.refresh();
		appGroup.refresh();
	}
	
	public final void validate(List<IStatus> errors)
	{
		if (getProject() != null) 
		{
			try 
			{
				if (!getProject().hasNature(SproutCoreNature.NATURE_ID))
				{
					IStatus status = ProjectUtil.createErrorStatus("Project does not have SproutCore nature");
					errors.add(status);
				}
			} 
			catch (CoreException e) {
				errors.add(e.getStatus());
			}
		}
		IResource resource = getResource();
		if (resource == null || !resource.exists()) {
			IStatus status = ProjectUtil.createErrorStatus(containerText.getText() + " does not exist.");
			errors.add(status);
		}
		projectBrowser.validate(errors);
		projectGroup.validate(errors);
		appGroup.validate(errors);
	}
	
	public final IProject getProject()
	{
		IResource resource = projectBrowser.getResource();
		if (resource != null && resource.exists()) {
			return resource.getProject();
		}
		return null;
	}
	
	public final String getProjectName()
	{
		return projectGroup.getName();
	}
	
	public final String getAppName()
	{
		return appGroup.getName();
	}
	
	/**
	 * 
	 */
	private abstract class SelectGroup implements IValidate
	{
		protected Combo combo;
		
		public String select(int index)
		{
			if (combo.getItemCount() > 0) 
			{
				String name = combo.getItem(index);
				combo.select(index);
				combo.setEnabled(combo.getItemCount() > 1);

				dispatchEvent(ChangeSelectionListener.CHANGE, 
						new ChangeSelectionEvent(this, combo));
				
				return name;
			}
			return null;
		}
		
		public final String select(String name)
		{
			if (combo.getItemCount() > 0) 
			{
				int index = 0;
				try {
					index = combo.indexOf(name);
				}
				catch (IllegalArgumentException e) {
					index = 0;
				}
				if (index < 0) {
					index = 0;
				}
				name = select(index);
				return name;
			}
			return null;
		}
		
		public abstract void createControl(Composite parent, int numColumns) 
		throws CoreException;
		
		public abstract void refresh() throws CoreException;
		
		public String getName() {
			return combo.getText();
		}
		
		public void validate(List<IStatus> errors) 
		{
			if (combo.getItemCount() == 0)
			{
				IStatus status = ProjectUtil.createErrorStatus(getNoItemsErrorMessage());
				errors.add(status);
			}
			else
			if (combo.getSelectionIndex() < 0)
			{
				IStatus status = ProjectUtil.createErrorStatus(getNoSelectionErrorMessage());
				errors.add(status);
			}
		}
		
		public abstract String getNoItemsErrorMessage();
		public abstract String getNoSelectionErrorMessage();
	}
	
	/**
	 * 
	 */
	private class ProjectGroup extends SelectGroup
	{
		private SproutCoreProject _scProject;
		
		public void createControl(Composite parent, int numColumns) 
		{
			final Label label = new Label(parent, SWT.NULL);
			label.setText("&ProjectName:");
			
			combo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
			final GridData gd = new GridData(GridData.FILL_HORIZONTAL);
			combo.setLayoutData(gd);
			combo.addSelectionListener(new SelectionAdapter() {

				public void widgetDefaultSelected(SelectionEvent e) 
				{
					widgetSelected(e);
				}

				public void widgetSelected(SelectionEvent e) 
				{
					Combo combo = (Combo)e.widget;
					String name = select(combo.getSelectionIndex());
					SproutCoreProject scProject = (SproutCoreProject)combo.getData(name);
					appGroup.setSproutCoreProject(scProject);
				}
				
			});
			
			if (numColumns > 2) {
				new Label(parent, SWT.NONE);
			}
			
			refresh();
		}
		
		public String select(int index)
		{
			String name = super.select(index);
			refreshApps(name);
			if (properties.getLastScProjectTarget() == null || 
				!properties.getLastScProjectTarget().equals(name))
			{
				properties.setLastScProjectTarget(name);
				try {
					PropertiesLoader.save(getProject(), properties);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return name;
		}
		
		private void refreshApps(String name)
		{
			_scProject = (SproutCoreProject)combo.getData(name);
			if (_scProject != null) {
				appGroup.setSproutCoreProject(_scProject);
			}
		}
		
		public void refresh() 
		{
			combo.removeAll();
			if (properties != null) 
			{
				TreeSet<SproutCoreProject> scProjects = properties.getScProjects();
				for (SproutCoreProject scProject:scProjects)
				{
					combo.add(scProject.getName());
					combo.setData(scProject.getName(), scProject);
				}
				select(properties.getLastScProjectTarget());
			}
			combo.setEnabled(combo.getItemCount() > 1);
		}

		@Override
		public String getNoItemsErrorMessage() {
			return "No SproutCore projects have been found.";
		}

		@Override
		public String getNoSelectionErrorMessage() {
			return "No SproutCore projects have been selected.";
		}
		
	}
	
	/**
	 * 
	 */
	private class AppGroup extends SelectGroup
	{
		private SproutCoreProject scProject;
		
		public void createControl(Composite parent, int numColumns) 
		throws CoreException 
		{
			final Label label = new Label(parent, SWT.NULL);
			label.setText("&AppName:");
			
			combo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
			final GridData gd = new GridData(GridData.FILL_HORIZONTAL);
			combo.setLayoutData(gd);
			combo.addSelectionListener(new SelectionAdapter() {

				public void widgetDefaultSelected(SelectionEvent e) 
				{
					widgetSelected(e);
				}

				public void widgetSelected(SelectionEvent e) 
				{
					Combo combo = (Combo)e.widget;
					select(combo.getSelectionIndex());
				}
				
			});
			
			if (numColumns > 2) {
				new Label(parent, SWT.NONE);
			}
			
			this.refresh();
		}
		
		public void setSproutCoreProject(SproutCoreProject scProject)
		{
			this.scProject = scProject;
			try {
				this.refresh();
			} catch (CoreException e) {
				e.printStackTrace();
			}
		}
		
		public String select(int index)
		{
			String name = super.select(index);
			if (scProject != null) 
			{
				if (scProject.getLastAppTarget() == null || 
					!scProject.getLastAppTarget().equals(name))
				{
					scProject.setLastAppTarget(name);
					try {
						PropertiesLoader.save(getProject(), properties);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			return name;
		}
		
		public void refresh() throws CoreException
		{
			if (combo != null)
			{
				combo.removeAll();
				if (scProject != null) 
				{
					IContainer[] appList = scProject.getApps(getProject());
					for (IContainer app:appList)
					{
						combo.add(app.getName());
						combo.setData(app.getName(), app);
					}
					select(scProject.getLastAppTarget());
				}
				combo.setEnabled(combo.getItemCount() > 1);
			}
		}
		
		@Override
		public String getNoItemsErrorMessage() {
			return "No SproutCore apps have been found in the project.";
		}

		@Override
		public String getNoSelectionErrorMessage() {
			return "No SproutCore apps have been selected.";
		}
	}
}
